package jn.chen.utils.poi.utils;

import cn.hutool.core.util.IdUtil;
import org.apache.poi.util.Units;
import org.apache.poi.xwpf.usermodel.*;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.STMerge;

import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;


/**
 * @className: PoiWordUtil
 * @author: chenLin
 * @version: 1.0.0
 * @description:
 * @date: 2022/11/1 0001 10:04
 */
public class PoiWordUtil {

    /**
     * @param doc        docx解析对象
     * @param tableIndex 第几个表格
     */
    public static XWPFTable getTable(XWPFDocument doc, int tableIndex) {
        return doc.getTables().get(tableIndex);
    }

    /**
     * 为表格插入行数，此处不处理表头，所以从第二行开始
     *
     * @param table     需要插入数据的表格
     * @param tableList 插入数据集合
     * @param index     在几行后开始插入数据 1为第一行
     */
    public static void insertTable(XWPFTable table, List<String[]> tableList, int index) {
        //创建与数据一致的行数
        for (int i = 0; i < tableList.size(); i++) {
            table.createRow();
        }
        int length = table.getRows().size() - index;
        for (int i = 0; i < length; i++) {
            XWPFTableRow newRow = table.getRow(i + index);
            List<XWPFTableCell> cells = newRow.getTableCells();
            for (int j = 0; j < cells.size(); j++) {
                XWPFTableCell cell = cells.get(j);
                cell.setText(tableList.get(i)[j]);
            }
        }
    }

    /**
     * 替换段落里面的变量
     *
     * @param doc    要替换的文档
     * @param params 参数
     */
    public static void replaceParams(XWPFDocument doc, Map<String, String> params) {
        Iterator<XWPFParagraph> iterator = doc.getParagraphsIterator();
        XWPFParagraph paragraph;
        while (iterator.hasNext()) {
            paragraph = iterator.next();
            replaceParam(paragraph, params);
        }
    }


    /**
     * 替换所有表格里面的变量
     *
     * @param doc 要替换的文档
     */
    public static void replaceAllTableParams(XWPFDocument doc, Map<String, String> testMap) {
        Iterator<XWPFTable> iterator = doc.getTablesIterator();
        XWPFTable table;
        List<XWPFTableRow> rows;
        List<XWPFTableCell> cells;
        List<XWPFParagraph> paras;
        while (iterator.hasNext()) {
            table = iterator.next();
            //判断表格是需要替换还是需要插入，判断逻辑有$为替换，表格无$为插入
            if (matcher(table.getText()).find()) {
                rows = table.getRows();
                for (XWPFTableRow row : rows) {
                    cells = row.getTableCells();
                    for (XWPFTableCell cell : cells) {
                        paras = cell.getParagraphs();
                        for (XWPFParagraph para : paras) {
                            replaceParam(para, testMap);
                        }
                    }
                }
            }
        }
    }

    /**
     * 替换表格里面的变量
     */
    public static void replaceTableParams(XWPFTable table, Map<String, String> testMap) {
        List<XWPFTableRow> rows;
        List<XWPFTableCell> cells;
        List<XWPFParagraph> paras;
        //判断表格是需要替换还是需要插入，判断逻辑有$为替换，表格无$为插入
        if (matcher(table.getText()).find()) {
            rows = table.getRows();
            for (XWPFTableRow row : rows) {
                cells = row.getTableCells();
                for (XWPFTableCell cell : cells) {
                    paras = cell.getParagraphs();
                    for (XWPFParagraph para : paras) {
                        replaceParam(para, testMap);
                    }
                }
            }
        }
    }

    /**
     * 正则匹配字符串
     *
     * @param str
     * @return
     */
    public static Matcher matcher(String str) {
        Pattern pattern = Pattern.compile("\\$\\{(.+?)\\}", Pattern.CASE_INSENSITIVE);
        return pattern.matcher(str);
    }

    /**
     * 替换段落里面的变量
     *
     * @param paragraph 要替换的段落
     * @param params    参数
     */
    public static void replaceParam(XWPFParagraph paragraph, Map<String, String> params) {
        List<XWPFRun> runs;
        Matcher matcher;
        StringBuilder runText = new StringBuilder();
//        if (matcher(paragraph.getParagraphText()).find()) {
            runs = paragraph.getRuns();
            int j = runs.size();
            for (int i = 0; i < j; i++) {
                runText.append(runs.get(0).toString());
                //保留最后一个段落，在这段落中替换值，保留段落样式
                if (!((j - 1) == i)) {
                    paragraph.removeRun(0);
                }
            }
            matcher = matcher(runText.toString());
            if (matcher.find()) {
                while ((matcher = matcher(runText.toString())).find()) {
                    runText = new StringBuilder(matcher.replaceFirst(String.valueOf(params.get(matcher.group(1)))));
                }
                runs.get(0).setText(runText.toString(), 0);
            }
//        }
    }

    /**
     * word单元格列合并 都以0开始计算
     *
     * @param table     表格
     * @param row       合并列所在行
     * @param startCell 开始列
     * @param endCell   结束列
     * @date 2022/11/1 0001 10:03
     */
    public static void mergeCellsHorizontal(XWPFTable table, int row, int startCell, int endCell) {
        for (int i = startCell; i <= endCell; i++) {
            XWPFTableCell cell = table.getRow(row).getCell(i);
            if (i == startCell) {
                // The first merged cell is set with RESTART merge value
                cell.getCTTc().addNewTcPr().addNewHMerge().setVal(STMerge.RESTART);
            } else {
                // Cells which join (merge) the first one, are set with CONTINUE
                cell.getCTTc().addNewTcPr().addNewHMerge().setVal(STMerge.CONTINUE);
            }
        }
    }

    /**
     * word单元格行合并
     *
     * @param table    表格
     * @param col      合并行所在列
     * @param startRow 开始行
     * @param endRow   结束行
     * @date 2022/11/1 0001 10:03
     */
    public static void mergeCellsVertically(XWPFTable table, int col, int startRow, int endRow) {
        for (int i = startRow; i <= endRow; i++) {
            XWPFTableCell cell = table.getRow(i).getCell(col);
            if (i == startRow) {
                cell.getCTTc().addNewTcPr().addNewVMerge().setVal(STMerge.RESTART);
            } else {
                cell.getCTTc().addNewTcPr().addNewVMerge().setVal(STMerge.CONTINUE);
            }
        }
    }


    /**
     * 替换图片
     * key 替换字段名称，与word中相同
     * value 图片地址
     */
    public static void doParagraphs(XWPFDocument doc, Map<String, String> imgMap) {
        List<XWPFParagraph> paragraphList = doc.getParagraphs();
        if (paragraphList != null && paragraphList.size() > 0) {
            for (XWPFParagraph paragraph : paragraphList) {
                List<XWPFRun> runs = paragraph.getRuns();
                for (XWPFRun run : runs) {
                    String text = run.getText(0);
                    if (text != null) {
                        String imgkey = text.replaceAll("\\{\\{@", "").replaceAll("}}", "");
                        String imgPath = imgMap.get(imgkey);
                        if (imgPath != null) {
                            if (imgPath.startsWith("http")) {
                                imgPath = saveToFile(imgPath);
                            }
                            saveLocalImg(run, imgPath);
                        }
                    }
                }
            }
        }
    }

    /**
     * 获取本地保存图片文件流
     *
     * @param imgPath
     * @return FileInputStream
     * @throws Exception
     */
    private static void saveLocalImg(XWPFRun run, String imgPath) {
        try (FileInputStream pictureData =  new FileInputStream(imgPath)) {
            run.setText("", 0);
            run.addPicture(pictureData,
                    Document.PICTURE_TYPE_PNG, "img.png", Units.toEMU(200), Units.toEMU(200));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }


    /**
     * 获取网络图片流
     *
     * @return
     */
    public static String saveToFile(String destUrl) {
        HttpURLConnection httpUrl = null;
        byte[] buf = new byte[1024];
        int size = 0;
        StringBuilder append = new StringBuilder()
                .append("D:/fjFile/")
                .append(IdUtil.simpleUUID())
                .append(".png");
        String fileUrl = append.toString();
        try {
            URL url = new URL(destUrl);
            httpUrl = (HttpURLConnection) url.openConnection();
            httpUrl.setConnectTimeout(5*1000);
            httpUrl.setReadTimeout(5*1000);
            httpUrl.connect();
            try (BufferedInputStream  bis = new BufferedInputStream(httpUrl.getInputStream());
                 FileOutputStream fos = new FileOutputStream(fileUrl)){
                while ((size = bis.read(buf)) != -1) {
                    fos.write(buf, 0, size);
                }
                fos.flush();
            } catch (IOException e) {
                e.printStackTrace();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }  finally {
            try {
                if(httpUrl!=null){
                    httpUrl.disconnect();
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return fileUrl;
    }


}
